/******************************************************************************************************
Copyright (C) 2017 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals date2ISOString, mainWindow, mass, qsTr, settings, Application, BatchConvertToMnova_Engine, CommonSlots, Dir, Document,
	File, MessageBox, ProgressDialog, TextStream, serialization, MnUi*/

/*jslint plusplus: true, continue:true, indent: 4*/

function BatchConvertToMnova() {
	"use strict";

	var dialog, showDialog, inputDirectory, outputDirectory, data, maskItems,
		parameters, batchConvertEngine, inputDirectoryKey, inputFileMaskKey,
		inputFileMaskIndexKey, outputDirectoryKey, currentIndexMask, batchResult;

	//Registry entries
	inputDirectoryKey = "Batch Convert To Mnova/Input Directory";
	inputFileMaskKey = "Batch Convert To Mnova/Input File Masks";
	inputFileMaskIndexKey = "Batch Convert To Mnova/Input File Mask Index";
	outputDirectoryKey = "Batch Convert To Mnova/Output Directory";

	//Load GUI
	dialog = Application.loadUiFile('ricares:batchConvertToMnova.ui');
	showDialog = true;

	//Load settings to the GUI
	data = settings.value(inputDirectoryKey, "");
	if (data !== "") {
		dialog.widgets.leInputDirectory.text = data;
	}

	data = settings.value(inputFileMaskKey, "");
	if (data !== "") {
		dialog.widgets.cbInputMask.items = JSON.parse(data);
	}

	data = settings.value(inputFileMaskIndexKey, "");
	if (data !== "") {
		dialog.widgets.cbInputMask.currentIndex = data;
	}

	data = settings.value(outputDirectoryKey, "");
	if (data !== "") {
		dialog.widgets.leOutputDirectory.text = data;
	}

	//Signals Management
	dialog.widgets.tbInputDirectory.clicked.connect(dialog.widgets.leInputDirectory, CommonSlots.onOpenDir);
	dialog.widgets.tbOutputDirectory.clicked.connect(dialog.widgets.leOutputDirectory, CommonSlots.onOpenDir);

	//Launch GUI and get the user response
	while (showDialog) {

		if (!dialog.exec()) {
			return;
		}

		//Input directory check control
		inputDirectory = new Dir(dialog.widgets.leInputDirectory.text);
		if (dialog.widgets.leInputDirectory.text === undefined || dialog.widgets.leInputDirectory.text.trim() === "") {
			dialog.widgets.leInputDirectory.setFocus();
			MessageBox.critical("The input directory is empty");
			continue;
		} else {
			inputDirectory = new Dir(dialog.widgets.leInputDirectory.text);
			if (!inputDirectory.exists) {
				MessageBox.critical("The input directory does not exist");
				continue;
			}
		}

		//File Mask check control
		if (dialog.widgets.cbInputMask.currentText === undefined || dialog.widgets.cbInputMask.currentText.trim() === "") {
			dialog.widgets.cbInputMask.setFocus();
			MessageBox.critical("The file mask is empty");
			continue;
		}

		//Output directory check control
		outputDirectory = new Dir(dialog.widgets.leOutputDirectory.text);
		if (dialog.widgets.leOutputDirectory.text === undefined || dialog.widgets.leOutputDirectory.text.trim() === "") {
			dialog.widgets.leOutputDirectory.setFocus();
			MessageBox.critical("The output directory is empty");
			continue;
		} else {
			outputDirectory = new Dir(dialog.widgets.leOutputDirectory.text);
			if (!outputDirectory.exists) {
				MessageBox.critical("The output directory does not exist");
				continue;
			}
		}
		showDialog = false;
	}

	//History items management
	currentIndexMask = dialog.widgets.cbInputMask.currentIndex;
	maskItems = BatchConvertToMnova.getHistoryFromCombobox(dialog.widgets.cbInputMask, dialog.widgets.cbInputMask.maxCount);
	if (maskItems.length > dialog.widgets.cbInputMask.items.length) {
		currentIndexMask++;
	}

	//Call to the engine
	parameters = {};
	parameters.inputDirectory = dialog.widgets.leInputDirectory.text;
	parameters.inputMask = dialog.widgets.cbInputMask.currentText;
	parameters.outputDirectory = dialog.widgets.leOutputDirectory.text;
	batchConvertEngine = new BatchConvertToMnova_Engine(parameters);
	batchResult = batchConvertEngine.process();

	//Write log to a text file
	BatchConvertToMnova.writeToFile(batchResult.directory + "/results.txt", batchResult.log);

	//Save values to the registry
	settings.setValue(inputDirectoryKey, dialog.widgets.leInputDirectory.text);
	settings.setValue(inputFileMaskKey, JSON.stringify(maskItems));
	settings.setValue(inputFileMaskIndexKey, currentIndexMask);
	settings.setValue(outputDirectoryKey, dialog.widgets.leOutputDirectory.text);
}

//----------------------------STATIC CLASS METHODS----------------------------
BatchConvertToMnova.getHistoryFromCombobox = function (aCombobox, aMaxHistoryItems) {
	"use strict";

	var i, flagFound, result;

	flagFound = false;
	result = [];

	if (aCombobox.items.length === aMaxHistoryItems) {
		result = aCombobox.items;
		result.shift();
		result.push(aCombobox.currentText);
	} else {
		for (i = 0; i < aCombobox.items.length; i++) {
			result.push(aCombobox.items[i]);
			if (aCombobox.items[i] === aCombobox.currentText) {
				flagFound = true;
			}
		}

		if (!flagFound) {
			result.push(aCombobox.currentText);
		}
	}

	return result;
};

BatchConvertToMnova.writeToFile = function (aPath, aContent) {
	"use strict";

	var logFile, resultFile;

	if (File.exists(aPath)) {
		File.remove(aPath);
	}
	logFile = new File(aPath);
	logFile.open(File.WriteOnly);
	resultFile = new TextStream(logFile);
	resultFile.codec =  "UTF-8";
	resultFile.write(aContent);
	resultFile.flush();
	logFile.close();
};

function BatchConvertToMnova_Engine(aParameters) {
	"use strict";

	//Set instance attributes from parameters
	this.inputDirectory = aParameters.inputDirectory.toString().replace(/\\/g, "/");
	this.inputMask = aParameters.inputMask;
	this.outputDirectory = aParameters.outputDirectory.toString().replace(/\\/g, "/");

	BatchConvertToMnova_Engine.prototype.process = function () {

		var i, j, date, log, inputDirectory, flagOpened, outputDirectory, flagError, result,
			pathPosition, foundFiles, foundFilesString, file, files, logHeader, activeMassItem,
			auxMnovaFilename, mnovaFilename, absolutePathMnovaFilename, documents, progressDialog,
			currentDocument, currentDocuments;

		//Header of the report
		date = date2ISOString(new Date()).toString().replace(new RegExp("\\:", 'g'), ".");
		logHeader = qsTr("Input Directory: {0}").format(this.inputDirectory);
		logHeader += "\n" + qsTr("Input File Mask: {0}").format(this.inputMask);
		logHeader += "\n" + qsTr("Output Directory: {0}").format(this.outputDirectory);
		logHeader += qsTr("Start Date: {0}").format(date);

		//Check Parameters
		flagError = false;
		log = "";
		if (this.inputDirectory === undefined || this.inputDirectory.trim() === "") {
			log += "\n" + qsTr("[ERROR]: The input directory is empty");
			flagError = true;
		} else {
			inputDirectory = new Dir(this.inputDirectory);
			if (!inputDirectory.exists) {
				log += "\n" + qsTr("[ERROR]: The input directory does not exist: {0}").format(this.inputDirectory);
				flagError = true;
			}
		}
		if (this.inputMask === undefined || this.inputMask.trim() === "") {
			log += "\n" + qsTr("[ERROR]: The input file mask is empty");
			flagError = true;
		}
		if (this.outputDirectory === undefined || this.outputDirectory.trim() === "") {
			log += "\n" + qsTr("[ERROR]: The output directory is empty");
			flagError = true;
		} else {
			outputDirectory = new Dir(this.outputDirectory);
			if (!outputDirectory.exists) {
				log += "\n" + qsTr("[ERROR]: The output directory does not exist: {0}").format(this.outputDirectory);
				flagError = true;
			}
		}

		//If the mandatory parameters were fine we start the batch process
		if (!flagError) {

			//Initialize progress bar
			progressDialog = new ProgressDialog();
			progressDialog.labelText = "Getting list of files ...";
			progressDialog.minimum = 0;
			progressDialog.maximum = 100;
			progressDialog.visible = false;

			//Get all list of files
			foundFiles = BatchConvertToMnova_Engine.getAllFiles(this.inputDirectory, this.inputMask, false, progressDialog);
			files = foundFiles[0];
			foundFilesString = foundFiles[1];
			log += "\n\n" + (qsTr("Total files found: {0}\n {1}".format(files.length, foundFilesString)));

			//Create result diretory
			outputDirectory.mkdir(date);
			outputDirectory.cd(date);
			log += "\n";

			//Get the number of documents before opening the data
			documents = mainWindow.documents();
			progressDialog.labelText = "Converting files ...";

			//Start process for each file
			for (i = 0; i < files.length; i++) {

				try {
					file = files[i];
					if (!File.exists(file)) {
						log += "\n\t" + qsTr("[ERROR]: The input file {0} does not exist").format(file);
					} else {

						//It creates the auxiliary document in order to create the molecule image
						//Calling this constructor the document will be hidden unless we call to the method mainWindow.addDocument
						currentDocument = new Document();

						//It locks the document to ensure all pointers like mainWindow.activeDocument and activeMolecule() points to the hidden document
						Application.lockDocument(currentDocument);

						//To work with the hiddend document feature we must open all document in the current document
						if (file.toLowerCase().endsWith(".mnova")) {
							flagOpened = serialization.open(file, "mnpag");
						} else {
							flagOpened = serialization.open(file);
						}

						if (!flagOpened) {
							//Update log
							log += "\n\t" + qsTr("[ERROR]: The input file {0} could not be opened").format(file);

							//Destroy the hidden document and unlocks the auxiliary document in order to restore the pointers
							currentDocument.destroy();
							Application.unlockDocument();

						} else {

							//Fetch MS if there is any in the current document
							activeMassItem = mass.activeItem();
							if (activeMassItem !== undefined) {
								mass.fetchFullDataset(activeMassItem);
							}

							//New Mnova filename management
							pathPosition = file.indexOf(inputDirectory.absPath);
							auxMnovaFilename = file.substring(pathPosition + inputDirectory.absPath.length, file.length);
							if (auxMnovaFilename[0] === "/") {
								auxMnovaFilename = auxMnovaFilename.toString().substring(1, auxMnovaFilename.toString().length);
							}
							mnovaFilename = auxMnovaFilename.replace(/\//g, "_");
							if (!mnovaFilename.toLowerCase().endsWith(".mnova")) {
								mnovaFilename += ".mnova";
							}
							absolutePathMnovaFilename = outputDirectory.absPath + "/" + mnovaFilename;

							//Check for items in the current document
							if (mainWindow.activeDocument.itemCount === 0) {
								log += "\n" + qsTr("[WARNING]: The input file {0} has generated a blank document").format(file);
							}

							//Save File Management
							serialization.save(absolutePathMnovaFilename);
							if (!File.exists(absolutePathMnovaFilename)) {
								log += "\n" + qsTr("[ERROR]: The input file {0} could not be saved at {1}").format(file, absolutePathMnovaFilename);
							} else {
								log += "\n" + qsTr("[INFO]: The input file {0} has been saved successfully at {1}").format(file, absolutePathMnovaFilename);
							}

							//Destroy the hidden document
							currentDocument.destroy();

							//It unlocks the auxiliary document in order to restore the pointers
							Application.unlockDocument();

							//Close all documents which index be below the maximum index when we start the process
							//This way works for dataset which open a new document or open in a new page of the active document
							currentDocuments = mainWindow.documents();
							for (j = documents.length; j < currentDocuments.length; j++) {
								currentDocuments[j].close();
							}
						}
					}
				} catch (e) {
					log += "\n" + qsTr("[ERROR]: Exception found processing file {0}: \n\t{1}").format(file, e);
				} finally {
					//Update progress bar
					BatchConvertToMnova_Engine.updateProgressBar(progressDialog, i, files.length, 10, 100);
				}
			}
			progressDialog.value = 100;
			progressDialog.visible = false;
			date = date2ISOString(new Date()).toString().replace(new RegExp("\\:", 'g'), ".");
			logHeader += "\n" + qsTr("Finish Date: {0}").format(date);
		}

		//Compose the result object
		result = {};
		result.log = logHeader + log;
		result.directory = outputDirectory.absPath;

		return result;
	};
}

//----------------------------STATIC CLASS METHODS----------------------------
BatchConvertToMnova_Engine.getFoundFilesString = function (aArray) {
	"use strict";

	var i, result;

	result = "";
	for (i = 0; i < aArray.length; i++) {
		result += "\t\t" + aArray[i];
		if (i !== aArray.length - 1) {
			result += "\n";
		}
	}

	return result;
};

BatchConvertToMnova_Engine.getAllFiles = function (aPathStartDirectory, aFileMask, aCheck, aProgressDialog) {
	'use strict';

	var i, j, k, dir, masks, files, found, result, resultString, output;

	dir = new Dir(aPathStartDirectory);
	result = [];
	output = [];
	resultString = "";
	if (dir.exists) {
		if (aFileMask !== undefined && aFileMask !== "") {
			masks = aFileMask.split("|");
			for (i = 0; i < masks.length && (aProgressDialog === undefined || !aProgressDialog.wasCanceled); i++) {
				if (masks[i] !== undefined && masks[i].trim() !== "") {
					files = dir.entryListAdv(masks[i]);
					if (aProgressDialog !== undefined) {
						aProgressDialog.update();
						mainWindow.processEvents();
					}

					if (i > 0) {
						resultString += "\n";
					}
					resultString += "\t" + qsTr("Total files found for mask {1}: {0}:".format(files.length, masks[i]));
					if (files.length > 0) {
						resultString += "\n";
						resultString += BatchConvertToMnova_Engine.getFoundFilesString(files);
					}
					for (j = 0; j < files.length && (aProgressDialog === undefined || !aProgressDialog.wasCanceled); j++) {
						found = false;
						if (result.length > 0) {
							if (aCheck !== undefined && aCheck) {
								return result;
							}
							for (k = 0; k < result.length && (aProgressDialog === undefined || !aProgressDialog.wasCanceled); k++) {
								if (result[k] === files[j]) {
									found = true;
								}
							}
						}
						if (!found) {
							result.push(files[j]);
							if (aCheck !== undefined && aCheck) {
								return result;
							}
						}
					}
				}

				if (aProgressDialog !== undefined) {
					BatchConvertToMnova_Engine.updateProgressBar(aProgressDialog, (i + 1), masks.length, 0, 10);
				}
			}
		}
	}

	output.push(result);
	output.push(resultString);

	return output;
};

BatchConvertToMnova_Engine.updateProgressBar = function (aProgressBar, aCurrentValue, aTotalValues, aStartValue, aEndValue) {
	"use strict";

	aProgressBar.value = Math.round(aStartValue + ((aCurrentValue / aTotalValues) * (aEndValue - aStartValue)));
	aProgressBar.update();
	mainWindow.processEvents();
};

if (this.MnUi && MnUi.scripts_common) {
	MnUi.scripts_common.scripts_common_ConvertToMnovaDocuments = BatchConvertToMnova;
}
